export function compose (...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

export function curry (fn) {
  const arity = fn.length
  return (function resolver () {
    const mem = Array.prototype.slice.call(arguments)
    return function () {
      const args = mem.slice()
      Array.prototype.push.apply(args, arguments)
      return (args.length >= arity ? fn : resolver).apply(null, args)
    }
  }())
}

export function applyMiddleware (...middlewares) {
  const chain = compose(...middlewares)
  return obj => new Proxy(obj, {
    get (target, p) {
      return chain(target[p])
    }
  })
}

export const applyChain = curry((vue, middlewares) => {
  if (!Array.isArray(middlewares)) {
    throw new Error(`middlewares expect be a Array`)
  }
  const middlewareChain = middlewares.map(item => item(vue))
  return applyMiddleware(...middlewareChain)
})
